#include "StdAfx.h"
#include "ActiveXUI.h"
#include "ActiveXCtrl.h"
#include "ActiveXWnd.h"

CActiveXUI::CActiveXUI(void) : m_pUnk(NULL), m_pControl(NULL), m_hwndHost(NULL), m_bCreated(false)
{
	m_szFixed.cx = m_szFixed.cy = 0;
}

CActiveXUI::~CActiveXUI(void)
{
	ReleaseControl();
}

LPCTSTR CActiveXUI::GetClass() const
{
	return _T("ActiveXUI");
}

SIZE CActiveXUI::EstimateSize(SIZE szAvailable)
{
	return m_szFixed;
}

static void PixelToHiMetric(const SIZEL* lpSizeInPix, LPSIZEL lpSizeInHiMetric)
{
#define HIMETRIC_PER_INCH   2540
#define MAP_PIX_TO_LOGHIM(x,ppli)   MulDiv(HIMETRIC_PER_INCH, (x), (ppli))
#define MAP_LOGHIM_TO_PIX(x,ppli)   MulDiv((ppli), (x), HIMETRIC_PER_INCH)
	int nPixelsPerInchX;    // Pixels per logical inch along width
	int nPixelsPerInchY;    // Pixels per logical inch along height
	HDC hDCScreen = ::GetDC(NULL);
	nPixelsPerInchX = ::GetDeviceCaps(hDCScreen, LOGPIXELSX);
	nPixelsPerInchY = ::GetDeviceCaps(hDCScreen, LOGPIXELSY);
	::ReleaseDC(NULL, hDCScreen);
	lpSizeInHiMetric->cx = MAP_PIX_TO_LOGHIM(lpSizeInPix->cx, nPixelsPerInchX);
	lpSizeInHiMetric->cy = MAP_PIX_TO_LOGHIM(lpSizeInPix->cy, nPixelsPerInchY);
}

void CActiveXUI::SetPos(RECT rc)
{
	if( !m_bCreated ) DelayedControlCreation();

	if( m_pUnk == NULL ) return;
	if( m_pControl == NULL ) return;

	m_rcItem = rc;

	SIZEL hmSize = { 0 };
	SIZEL pxSize = { 0 };
	pxSize.cx = m_rcItem.right - m_rcItem.left;
	pxSize.cy = m_rcItem.bottom - m_rcItem.top;
	PixelToHiMetric(&pxSize, &hmSize);

	if( m_pUnk != NULL ) {
		m_pUnk->SetExtent(DVASPECT_CONTENT, &hmSize);
	}
	if( m_pControl->m_pInPlaceObject != NULL ) {
		CStdRect rcItem = m_rcItem;
		if( !m_pControl->m_bWindowless ) rcItem.ResetOffset();
		m_pControl->m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem);
	}
	if( !m_pControl->m_bWindowless ) {
		ASSERT(m_pControl->m_pWindow);
		::MoveWindow(*m_pControl->m_pWindow, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, TRUE);
	}
}

void CActiveXUI::DoPaint(HDC hDC, const RECT& /*rcPaint*/)
{
	if( m_pControl != NULL && m_pControl->m_bWindowless && m_pControl->m_pViewObject != NULL )
	{
		m_pControl->m_pViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, hDC, (RECTL*) &m_rcItem, (RECTL*) &m_rcItem, NULL, NULL); 
	}
}

void CActiveXUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
	if( _tcscmp(pstrName, _T("clsid")) == 0 ) CreateControl(pstrValue);
	else if( _tcscmp(pstrName, _T("width")) == 0 ) SetWidth(_ttoi(pstrValue));
	else if( _tcscmp(pstrName, _T("height")) == 0 ) SetHeight(_ttoi(pstrValue));
	else CControlUI::SetAttribute(pstrName, pstrValue);
}

LRESULT CActiveXUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled)
{
	if( m_pControl == NULL ) return 0;
	ASSERT(m_pControl->m_bWindowless);
	if( !m_pControl->m_bInPlaceActive ) return 0;
	if( m_pControl->m_pInPlaceObject == NULL ) return 0;
	bool bWasHandled = true;
	if( (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) || uMsg == WM_SETCURSOR )
	{
		// Mouse message only go when captured or inside rect
		DWORD dwHitResult = m_pControl->m_bCaptured ? HITRESULT_HIT : HITRESULT_OUTSIDE;
		if( dwHitResult == HITRESULT_OUTSIDE && m_pControl->m_pViewObject != NULL ) {
			IViewObjectEx* pViewEx = NULL;
			m_pControl->m_pViewObject->QueryInterface(IID_IViewObjectEx, (LPVOID*) &pViewEx);
			if( pViewEx != NULL ) {
				POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
				pViewEx->QueryHitPoint(DVASPECT_CONTENT, &m_rcItem, ptMouse, 0, &dwHitResult);
				pViewEx->Release();
			}
		}
		if( dwHitResult != HITRESULT_HIT ) return 0;
		if( uMsg == WM_SETCURSOR ) bWasHandled = false;
	}
	else if( uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST )
	{
		// Keyboard messages just go when we have focus
		if( !m_pControl->m_bFocused ) return 0;
	}
	else
	{
		switch( uMsg ) {
	  case WM_HELP:
	  case WM_CONTEXTMENU:
		  bWasHandled = false;
		  break;
	  default:
		  return 0;
		}
	}
	LRESULT lResult = 0;
	HRESULT Hr = m_pControl->m_pInPlaceObject->OnWindowMessage(uMsg, wParam, lParam, &lResult);
	if( Hr == S_OK ) bHandled = bWasHandled;
	return lResult;
}

void CActiveXUI::SetWidth(int cx)
{
	m_szFixed.cx = cx;
}

void CActiveXUI::SetHeight(int cy)
{
	m_szFixed.cy = cy;
}

bool CActiveXUI::CreateControl(LPCTSTR pstrCLSID)
{
	CLSID clsid = { 0 };
	OLECHAR szCLSID[100] = { 0 };
#ifndef _UNICODE
	::MultiByteToWideChar(::GetACP(), 0, pstrCLSID, -1, szCLSID, lengthof(szCLSID) - 1);
#else
	_tcsncpy(szCLSID, pstrCLSID, lengthof(szCLSID) - 1);
#endif
	if( pstrCLSID[0] == '{' ) ::CLSIDFromString(szCLSID, &clsid);
	else ::CLSIDFromProgID(szCLSID, &clsid);
	return CreateControl(clsid);
}

bool CActiveXUI::CreateControl(const CLSID clsid)
{
	ASSERT(clsid!=IID_NULL);
	if( clsid == IID_NULL ) return false;
	// NOTE: We do *not* actually create the ActiveX at this point; we merely
	//       make a note of the CLSID and delays the creating until we know for 
	//       sure that the entire user-interface is running.
	m_bCreated = false;
	m_clsid = clsid;
	return true;
}

void CActiveXUI::ReleaseControl()
{
	m_hwndHost = NULL;
	if( m_pUnk != NULL ) {
		IObjectWithSite* pSite = NULL;
		m_pUnk->QueryInterface(IID_IObjectWithSite, (LPVOID*) &pSite);
		if( pSite != NULL ) {
			pSite->SetSite(NULL);
			pSite->Release();
		}
		m_pUnk->Close(OLECLOSE_NOSAVE);
		m_pUnk->SetClientSite(NULL);
		m_pUnk->Release(); 
		m_pUnk = NULL;
	}
	if( m_pControl != NULL ) {
		m_pControl->m_pOwner = NULL;
		m_pControl->Release();
		m_pControl = NULL;
	}
	m_pManager->RemoveMessageFilter(this);
}

bool CActiveXUI::DelayedControlCreation()
{
	ReleaseControl();
	// At this point we'll create the ActiveX control
	m_bCreated = true;
	IOleControl* pOleControl = NULL;
	HRESULT Hr = ::CoCreateInstance(m_clsid, NULL, CLSCTX_ALL, IID_IOleControl, (LPVOID*) &pOleControl);
	ASSERT(SUCCEEDED(Hr));
	if( FAILED(Hr) ) return false;
	pOleControl->QueryInterface(IID_IOleObject, (LPVOID*) &m_pUnk);
	pOleControl->Release();
	if( m_pUnk == NULL ) return false;
	// Create the host too
	m_pControl = new CActiveXCtrl();
	m_pControl->m_pOwner = this;
	// More control creation stuff
	DWORD dwMiscStatus = 0;
	m_pUnk->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
	IOleClientSite* pOleClientSite = NULL;
	m_pControl->QueryInterface(IID_IOleClientSite, (LPVOID*) &pOleClientSite);
	CSafeRelease<IOleClientSite> RefOleClientSite = pOleClientSite;
	// Initialize control
	if( (dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) != 0 ) m_pUnk->SetClientSite(pOleClientSite);
	IPersistStreamInit* pPersistStreamInit = NULL;
	m_pUnk->QueryInterface(IID_IPersistStreamInit, (LPVOID*) &pPersistStreamInit);
	if( pPersistStreamInit != NULL ) {
		Hr = pPersistStreamInit->InitNew();
		pPersistStreamInit->Release();
	}
	if( FAILED(Hr) ) return false;
	if( (dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST) == 0 ) m_pUnk->SetClientSite(pOleClientSite);
	// Grab the view...
	Hr = m_pUnk->QueryInterface(IID_IViewObjectEx, (LPVOID*) &m_pControl->m_pViewObject);
	if( FAILED(Hr) ) Hr = m_pUnk->QueryInterface(IID_IViewObject2, (LPVOID*) &m_pControl->m_pViewObject);
	if( FAILED(Hr) ) Hr = m_pUnk->QueryInterface(IID_IViewObject, (LPVOID*) &m_pControl->m_pViewObject);
	// Activate and done...
	m_pUnk->SetHostNames(OLESTR("UIActiveX"), NULL);
	if( (dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME) == 0 ) {
		Hr = m_pUnk->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, pOleClientSite, 0, m_pManager->GetPaintWindow(), &m_rcItem);
		::RedrawWindow(m_pManager->GetPaintWindow(), &m_rcItem, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_INTERNALPAINT | RDW_FRAME);
	}
	IObjectWithSite* pSite = NULL;
	m_pUnk->QueryInterface(IID_IObjectWithSite, (LPVOID*) &pSite);
	if( pSite != NULL ) {
		pSite->SetSite(static_cast<IOleClientSite*>(m_pControl));
		pSite->Release();
	}
	return SUCCEEDED(Hr);
}

HRESULT CActiveXUI::GetControl(const IID iid, LPVOID* ppRet)
{
	ASSERT(ppRet!=NULL);
	ASSERT(*ppRet==NULL);
	if( ppRet == NULL ) return E_POINTER;
	if( m_pUnk == NULL ) return E_PENDING;
	return m_pUnk->QueryInterface(iid, (LPVOID*) ppRet);
}
